package memoriaintermedia;

import java.util.Iterator;
import memoriaintermedia.*;
import java.util.LinkedList;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Representa una abstracción de un Gestor de la Memoria Intermedia*/

/**
 *
 * @author Christian
 */
public class GestorMemoriaIntermedia {

    static final Logger logger = Logger.getLogger("Gestion de Buffer");
    /** Representa a los bloques de la memoria intermedia o buffer. */
    private Pagina[] buffer;
    /** Numero de paginas en total en el buffer. */
    private int numeroPaginas;
    /** Posición en el buffer de la ultima pagina colocada. */ /*indice de la posicion
     donde nos encontramos actualmente al inicio esta vacion en -1*/
    private int posicionUltimaPagina = -1;
    /** cola para el control de sustitución basada en LRU. *//*controla el uso de
     la memoria intermedia*/
    private Queue colaLRU;
    //numero de paginas clavadas en el buffer
    private int paginasclavadas=0;
    // cola para el control basada en MRU
    private LinkedList colaMRU;

    /**
     * Crea un nuevo Gestor de memoria intermedia.
     * 
     * @param numeroPaginas cantidad de paginas en total en el buffer.
     */
    public GestorMemoriaIntermedia(int numeroPaginas) {
        this.numeroPaginas = numeroPaginas;
        // Crear el buffer con paginas vacias
        buffer = new Pagina[numeroPaginas];
        // Crear una cola FIFO
        colaLRU = new LinkedList<Integer>();
//        bufferActualizados=new LinkedList<Pagina>();
        // crea una cola LIFO
        colaMRU = new LinkedList<Integer>();
        
    }

    /**
     * Devuelve el numero de paginas clavadas del buffer.
     */
    public int getPaginasclavadas() {
        return paginasclavadas;
    }

    /**
     * Devuelve una pagina conociendo el numero de la página.
     * 
     * @param numeroPagina el id de la pagina que queremos devolver
     * @return la página del id de pagina proporcionado.
     */
    public Pagina recuperarPaginaDelBuffer(int numeroPagina) {/*busca la pagina
     en la memoria intermedia, el numero de la pagina != posicion de la pagina
     en la memoria intermedia*/
        // Verificar si la pagina esta en el buffer
        int posicion = posicionDePaginaEnBuffer(numeroPagina);
        if (posicion >= 0) // la pagina esta en el buffer
        {
            /*la pagina encontrada se tiene que volver en la mas recientemente
             utilizada 5,6,10,11,12 si fuera 10 10,5,6,11,12*/
            // Marcar Pagina o bloque como recientemente referenciado
            colaLRU.remove(numeroPagina);
            colaLRU.add(numeroPagina);
            // devolver la pagina del buffer al solicitante
            return buffer[posicion];
        }
        return null;
    }
    
    /**
     * Determina si la pagina ha sido modificada.
     * 
     * @param pagina la pagina que sera verificada.
     * @return true si la pagina ha sido modificada false si no lo fue.
     */
    public Boolean PaginaActualizada(Pagina pagina){
        return pagina.getActualizado();
    }
    
    /**
     * Determina si se pueden agregar paginas clavadas en el buffer.
     * 
     * @return true si es posible agregar y false si no.
     */
    public Boolean isclavadasbuffer(){
        if (this.paginasclavadas<=(this.buffer.length)-2){return true;}
        else{return false;}
    }
    
    
    public Pagina InsertarPagina(Pagina pagina){
        posicionUltimaPagina++;
        buffer[posicionUltimaPagina] = pagina;
        // Actualizar la LRU
        colaLRU.remove(posicionUltimaPagina);//no realiza ningun cambio
        colaLRU.add(posicionUltimaPagina);
        return null;
    }
    
    public Pagina InsertarPaginaMRU(Pagina pagina){
        posicionUltimaPagina++;
        buffer[posicionUltimaPagina] = pagina;
        
        colaMRU.addLast(pagina.getNumeroPagina());
        return null;
    }
    
    
    public Pagina ponerPaginaEnBufferMRU(Pagina pagina){
        if (estaPaginaEnBuffer(pagina)) {/*averiguar si se tiene q actualizar la LRU*/
            colaMRU.removeFirst();//no realiza ningun cambio
            colaMRU.addLast(pagina.getNumeroPagina());
            // no descartar ninguna pagina o bloque 
            return null;
        } // Si la pagina no esta en el buffer, pero con espacio en el buffer
        else if (!bufferLLeno()) {
            /*********************************************************/
            if (pagina.isPaginaclavada()){
                if (isclavadasbuffer()){return InsertarPaginaMRU(pagina);}
                else {
                    System.out.println("No se puede agregar pagina");
                    return null;}
            }
            else{return InsertarPaginaMRU(pagina);}
        } // Si la pagina no esta en el buffer, pero sin espacio en el buffer
        else {
            // recuperar la pagina a descartar
            /*identificar el id pagina a descartar de la cola LRU*/
            int numeroPaginaDescartada = (Integer) colaMRU.removeLast();
            /*conociendo el id de pagina ubicar la posicion de la pagina 
             en el buffer */
            int posicion = posicionDePaginaEnBuffer(numeroPaginaDescartada);
            // if 
            /*extraer la pagina a descartar del buffer*/
            Pagina paginaDescartada = buffer[posicion];
            if (paginaDescartada.isPaginaclavada()){
                //no puede eliminarse
                colaMRU.remove(paginaDescartada.getNumeroPagina());
                colaMRU.addFirst(numeroPaginaDescartada);
                return ponerPaginaEnBufferMRU(pagina);
            }
            else{// se elimina
                buffer[posicion] = pagina;
            /*hacer que el id de la nueva pagina sea el mas recientemente 
             referenciado*/
            //colaMRU.removeLast();
            colaMRU.addLast(pagina.getNumeroPagina());
            return paginaDescartada;
            }
        }
    }
    
    
    public void InnerJoin(Pagina[] A,Pagina[] B){
        for (int i=0;i<A.length;i++){
            A[i].setPaginaclavada(true);
            ponerPaginaEnBufferMRU(A[i]);
            for (int k=0;k<B.length;k++){
                ponerPaginaEnBufferMRU(B[k]);
            }
            A[i].setPaginaclavada(false);
            // pagina MRU
            colaMRU.remove(A[i].getNumeroPagina());//no realiza ningun cambio
            colaMRU.addLast(A[i].getNumeroPagina());
        }
    }
    
    
    
    
    /**
     * Poner una pagina en el buffer de la memoria intermedia.
     * 
     * @param pagina la pagina a ser insertado en el buffer.
     * @return la pagina que sera descartada del buffer si es necesario.
     */
    public Pagina ponerPaginaEnBuffer(Pagina pagina) {/*tres casos:1.si la pagina
     ya esta en el buffer no se hace nada 2. si no esta en el buffer y hay espacio
     en el buffer 3.si no esta y no hay espacio, los mas recientemente utilizados
     se ubican al inicio de la cola, los menos al final*/
        // Si la pagina esta en el buffer
        
/*>>>*/        if (estaPaginaEnBuffer(pagina)) {/*averiguar si se tiene q actualizar la LRU*/
            // no descartar ninguna pagina o bloque 
            return null;
        } // Si la pagina no esta en el buffer, pero con espacio en el buffer
        else if (!bufferLLeno()) {
            /*********************************************************/
            if (pagina.isPaginaclavada()){
                if (isclavadasbuffer()){return InsertarPagina(pagina);}
                else {
                    System.out.println("No se puede agregar pagina");
                    return null;}
            }
            else{return InsertarPagina(pagina);}
        } // Si la pagina no esta en el buffer, pero sin espacio en el buffer
        else {
            // recuperar la pagina a descartar
            /*identificar el id pagina a descartar de la cola LRU*/
            int numeroPaginaDescartada = (Integer) colaLRU.remove();
            /*conociendo el id de pagina ubicar la posicion de la pagina 
             en el buffer */
            int posicion = posicionDePaginaEnBuffer(numeroPaginaDescartada);
            // if 
            /*extraer la pagina a descartar del buffer*/
            Pagina paginaDescartada = buffer[posicion];
            if (paginaDescartada.isPaginaclavada()){
                //no puede eliminarse
                colaLRU.add(numeroPaginaDescartada);
                return ponerPaginaEnBuffer(pagina);
            }
            else{// se elimina
                buffer[posicion] = pagina;
            /*hacer que el id de la nueva pagina sea el mas recientemente 
             referenciado*/
            colaLRU.add(pagina.getNumeroPagina());
            return paginaDescartada;
            }
        }
    }

    private int posicionDePaginaEnBuffer(int numeroPagina) {
        int posicion = -1;
        for (int i = 0; i < buffer.length; i++) {
            if ((buffer[i] != null)
                    && (numeroPagina == buffer[i].getNumeroPagina())) {
                posicion = i;
                break;
            }
        }
        return posicion;

    }

    private boolean estaPaginaEnBuffer(Pagina pagina) {
        return (posicionDePaginaEnBuffer(pagina.getNumeroPagina()) != -1);
    }

    private boolean bufferLLeno() {
        return numeroPaginas - 1 == posicionUltimaPagina;
    }

    @Override
    public String toString() {
        String resultado = "";
        for (int i = 0; i < buffer.length; i++) {
            resultado += "Pagina[" + i + "]: " + buffer[i] + "\n";
        }
        return resultado + "\n" + this.colaLRU;
    }

    public static void main(String[] args) {
         //Crear un gestor de memoria
        System.out.println("EJERCICIOS GESTOR DE MEMORIA INTERMEDIA");
        System.out.println("=========================================");
        System.out.println("EJERCICIO 1");
        GestorMemoriaIntermedia gestorBuffer = new GestorMemoriaIntermedia(6);
          // Paginas del Disco
        System.out.println("Paginas del Disco: ");
        Pagina[] paginas = new Pagina[10];
        for (int i = 0; i < paginas.length; i++) {
            Pagina pagina = new Pagina(i, "p" + i);
            paginas[i] = pagina;
            System.out.println(pagina);
        }
        for (int i = 0; i < paginas.length; i++) {
            gestorBuffer.ponerPaginaEnBuffer(paginas[i]);
        }

        System.out.println("Contenido del Gestor: ");
        System.out.println(gestorBuffer);
        
        System.out.println("Modificar contenido de una pagina: ");
        gestorBuffer.buffer[4].setDatos("p100");
        
        System.out.println("Modificar contenido de una pagina con el mismo contenido: ");
        gestorBuffer.buffer[2].setDatos("p8");
        
        System.out.println("Contenido del Gestor despues de modificar: ");
        System.out.println(gestorBuffer);
        
        System.out.println("Resultado de una pagina modificada: ");
        System.out.println(gestorBuffer.buffer[4].getActualizado());
        
        System.out.println("Resultado de una pagina modificada con el mismo contenido: ");
        System.out.println(gestorBuffer.buffer[2].getActualizado());
        
        System.out.println("Resultado de una pagina no modificada: ");
        System.out.println(gestorBuffer.buffer[5].getActualizado());
        
        System.out.println("=========================================");
        
        
        System.out.println("=========================================");
        System.out.println("EJERCICIO 2");
        GestorMemoriaIntermedia gestorBuffer1 = new GestorMemoriaIntermedia(6);
        System.out.println("Paginas del Disco: ");
        Pagina[] pag = new Pagina[10];
        for (int i = 0; i < pag.length; i++) {
            Pagina pagina = new Pagina(i, "p" + i);
            pag[i] = pagina;
            System.out.println(pagina);
        }
        
        // hacer que dos paginas sean clavadas
        pag[0].setPaginaclavada(true);gestorBuffer1.paginasclavadas++;
        pag[1].setPaginaclavada(true);gestorBuffer1.paginasclavadas++;
        pag[2].setPaginaclavada(true);gestorBuffer1.paginasclavadas++;
        
        for (int i = 0; i < pag.length; i++) {
            gestorBuffer1.ponerPaginaEnBuffer(pag[i]);
        }

        System.out.println("Contenido del Gestor despues de poner bloques: ");
        System.out.println(gestorBuffer1);
        System.out.println("=========================================");
        
        
        
        
        System.out.println("=========================================");
        System.out.println("EJERCICIO 3");
        
        GestorMemoriaIntermedia gestorBuffer2 = new GestorMemoriaIntermedia(12);
        System.out.println("Paginas del Disco para prestatarios: ");
        Pagina[] prestatarios = new Pagina[3];
        for (int i = 0; i < prestatarios.length; i++) {
            Pagina pagina = new Pagina(i, "p" + i);
            prestatarios[i] = pagina;
            System.out.println(pagina);
        }
        
        System.out.println("Paginas del Disco para clientes: ");
        Pagina[] clientes = new Pagina[15];
        for (int i = 0; i < clientes.length; i++) {
            Pagina pagina = new Pagina(i+3, "c" + (i+3));
            clientes[i] = pagina;
            System.out.println(pagina);
        }
        // Metodo que se encargara de realizar el inner join entre prestatarios y clientes
        gestorBuffer2.InnerJoin(prestatarios, clientes);
        System.out.println("Contenido del Gestor despues de poner bloques: ");
        System.out.println(gestorBuffer2);
        System.out.println(gestorBuffer2.colaMRU);
        
        System.out.println("=========================================");
    }
}
